package dream

import (
	"context"
	"sync"
	"time"

	commonIface "github.com/taubyte/tau/core/common"
	"github.com/taubyte/tau/core/kvdb"
	"github.com/taubyte/tau/p2p/peer"
	commonSpecs "github.com/taubyte/tau/pkg/specs/common"
)

type UniverseState int

const (
	UniverseStateStopped UniverseState = iota
	UniverseStateStarting
	UniverseStateRunning
	UniverseStateStopping
)

type Universe struct {
	ctx  context.Context
	ctxC context.CancelFunc

	multiverse *Multiverse

	lock      sync.RWMutex
	name      string
	root      string
	id        string
	swarmKey  []byte
	all       []peer.Node
	closables []commonIface.Service
	lookups   map[string]*NodeInfo
	portShift int
	service   map[string]*serviceInfo
	simples   map[string]*Simple

	keepRoot bool
	state    UniverseState

	// Disk usage cache
	diskUsageCache     int64
	diskUsageCacheTime time.Time
	diskUsageCacheLock sync.RWMutex
}

type serviceInfo struct {
	nodes map[string]commonIface.Service
}

type handlers struct {
	service ServiceCreate
	client  ClientCreate
}

type ServiceCreate func(*Universe, *commonIface.ServiceConfig) (commonIface.Service, error)
type ClientCreate func(peer.Node, *commonIface.ClientConfig) (commonIface.Client, error)

var Registry *handlerRegistry

type handlerRegistry struct {
	registry map[string]*handlers
	lock     sync.RWMutex
}

// Order of params important!
type FixtureHandler func(universe *Universe, params ...interface{}) error

type FixtureVariable struct {
	Name        string
	Alias       string
	Description string
	Required    bool
}

type FixtureDefinition struct {
	Description string
	ImportRef   string
	Variables   []FixtureVariable
	BlockCLI    bool
	Internal    bool
}

type ClientCreationMethod func(*commonIface.ClientConfig) error

type SimpleConfig struct {
	commonIface.CommonConfig
	Clients map[string]*commonIface.ClientConfig
}

type NodeInfo struct {
	DbFactory kvdb.Factory
	Node      peer.Node
	Name      string
	Ports     map[string]int
}

type Config struct {
	Services map[string]commonIface.ServiceConfig
	Simples  map[string]SimpleConfig
}

type UniverseConfig struct {
	Name     string
	Id       string
	KeepRoot bool
}

type Status map[string]UniverseStatus

type UniverseStatus struct {
	Root      string              `json:"root"`
	SwarmKey  []byte              `json:"swarm-key"`
	NodeCount int                 `json:"node-count"`
	Simples   []string            `json:"simples"`
	Nodes     map[string][]string `json:"nodes"`
	Services  []ServiceStatus     `json:"services"`
}

type ServiceStatus struct {
	Name   string `json:"name"`
	Copies int    `json:"copies"`
}

type Multiverse struct {
	ctx  context.Context
	ctxC context.CancelFunc

	name string

	loadPersistent bool

	universes     map[string]*Universe
	universesLock sync.RWMutex
}

type Simple struct {
	peer.Node
	clients map[string]commonIface.Client
	lock    sync.RWMutex
}

// Deprecated use Map[string]*commonIface.ClientConfig{}
type SimpleConfigClients struct {
	TNS     *commonIface.ClientConfig
	Auth    *commonIface.ClientConfig
	Seer    *commonIface.ClientConfig
	Patrick *commonIface.ClientConfig
	Monkey  *commonIface.ClientConfig
	Hoarder *commonIface.ClientConfig
}

func (s SimpleConfigClients) Compat() map[string]*commonIface.ClientConfig {
	newClientConfig := make(map[string]*commonIface.ClientConfig)

	if s.TNS != nil {
		newClientConfig[commonSpecs.TNS] = s.TNS
	}

	if s.Auth != nil {
		newClientConfig[commonSpecs.Auth] = s.Auth
	}

	if s.Seer != nil {
		newClientConfig[commonSpecs.Seer] = s.Seer
	}

	if s.Patrick != nil {
		newClientConfig[commonSpecs.Patrick] = s.Patrick
	}

	if s.Monkey != nil {
		newClientConfig[commonSpecs.Monkey] = s.Monkey
	}

	if s.Hoarder != nil {
		newClientConfig[commonSpecs.Hoarder] = s.Hoarder
	}

	return newClientConfig
}

// State management methods for Universe
func (u *Universe) State() UniverseState {
	u.lock.RLock()
	defer u.lock.RUnlock()
	return u.state
}

func (u *Universe) Running() bool {
	return u.State() == UniverseStateRunning
}

func (u *Universe) Stopped() bool {
	return u.State() == UniverseStateStopped
}

func (u *Universe) Starting() bool {
	return u.State() == UniverseStateStarting
}

func (u *Universe) Stopping() bool {
	return u.State() == UniverseStateStopping
}

// String representation of state for debugging
func (s UniverseState) String() string {
	switch s {
	case UniverseStateStopped:
		return "stopped"
	case UniverseStateStarting:
		return "starting"
	case UniverseStateRunning:
		return "running"
	case UniverseStateStopping:
		return "stopping"
	default:
		return "unknown"
	}
}
